#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <getopt.h>

#include "../error.h"
#include "../main.h"
#include "../util.h"
#include "try-login.h"
#include "../action/probe.h"
#include "../action/pageinfo.h"
#include "../action/encrypt.h"
#include "../action/login.h"

#define DEFAULT_PROBE_ADDRESS   "http://connect.rom.miui.com/generate_204"
#define DEFAULT_PROBE_MODE      PROBE_MODE_STATUS
#define DEFAULT_PROBE_CONTENT   ""
#define DEFAULT_PROBE_STATUS    204

_Noreturn void verb_try_login_usage(FILE *restrict out) {
    fputs("Usage: " PROGRAM_NAME " try-login [OPTIONS] [USERNAME] [PASSWORD]\n", out);
    fputs("Probe the network connectivity and log in if unlogined\n", out);
    fputs("Username and password is required.\n", out);
    fputs("\n", out);
    fputs("Options:\n", out);
    fputs("  -h, --help                 display this help and exit\n", out);
    fputs("\n", out);
    fputs("Exit status:\n", out);
    fputs(" 0  if logined successfully\n", out);
    fputs(" 1  if current network don't need login\n", out);
    fputs(" 2  if logined failed\n", out);
    fputs(" 3  if failed to connect to the network\n", out);
    fputs(" 4  if other error happended\n", out);
    exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}

int verb_try_login(const int argc, char* const* argv) {
    int lose = 0;
    static struct option long_options[] = {
        {"help",      no_argument,        NULL, 'h' },
        {0,           0,                  0,     0 }
    };
    const char* username = NULL;
    const char* password = NULL;
    
    while (1) {
        int c = getopt_long(argc, argv, "-h", long_options, NULL);

        if (c == -1) {
            break;
        }

        switch (c) {
            case 'h':
                verb_try_login_usage(stdout);
                break;
            
            case 1:
                if (username != NULL) {
                    if (password != NULL) {
                        error(0, 0, "exceed argument: %s", optarg);
                        verb_try_login_usage(stderr);
                    }
                    password = optarg;
                    break;
                }
                username = optarg;
                break;

            default:
                lose = 1;
                break;
        }
    }

    if (lose) {
        verb_try_login_usage(stderr);
        return EXIT_FAILURE;
    }
    
    if (username == NULL) {
        if (GLOBAL_ARGS.auth_username == NULL) { 
            error(0, 0, "No username provided.");
            exit(EXIT_FAILURE);
        } else {
            username = GLOBAL_ARGS.auth_username;
        }
    }
    
    if (password == NULL) { 
        if (GLOBAL_ARGS.auth_password == NULL) { 
            error(0, 0, "No password provided.");
            exit(EXIT_FAILURE);
        } else {
            password = GLOBAL_ARGS.auth_password;
        }
    }
    
    action_probe_result probe_result = action_probe(
        GLOBAL_ARGS.bind_interface,
        DEFAULT_PROBE_ADDRESS,
        DEFAULT_PROBE_MODE,
        DEFAULT_PROBE_CONTENT,
        DEFAULT_PROBE_STATUS
    );
    switch (probe_result.status) {
        case ACTION_PROBED_SUCCESSFULLY:
            action_probe_result_free(&probe_result);
            return 1;
        case ACTION_PROBED_LOGIN_PAGE:
            break;
        case ACTION_PROBED_NOTHING:
            action_probe_result_free(&probe_result);
            return 4;
        case ACTION_PROBEING_FAILURE:
            action_probe_result_free(&probe_result);
            return 3;
        default:
            assert(0);
            exit(1);
            break;
    }
    
    action_pageinfo_result pageinfo_result = action_pageinfo(
        GLOBAL_ARGS.bind_interface,
        probe_result.host,
        probe_result.search
    );
    
    switch (pageinfo_result.status) {
        case ACTION_FETCHING_FAILURE:
            return 3;
        case ACTION_PAGEINFO_PARSE_FAILURE:
            return 4;
        case ACTION_FETCHED_SUCCESSFULLY:
            break;
        default:
            assert(0);
            exit(1);
            break;
    }
    
    const char* encrypted_password = NULL;
    if (pageinfo_result.passwordEncrypt) {
        action_encrypt_result encrypt_result = action_encrypt(
            password,
            pageinfo_result.publicKeyExponent,
            pageinfo_result.publicKeyModulus
        );
        
        if (encrypt_result.status == ACTION_ENCRYPTING_PARSE_FAILURE) {
            return 4;
        }
        // TODO add more status
        
        encrypted_password = encrypt_result.encrypted_string;
    } else {
        encrypted_password = password;
    }
    
    action_login_result login_result = action_login(
        GLOBAL_ARGS.bind_interface,
        probe_result.host,
        probe_result.search,
        username,
        encrypted_password
    );
    
    switch (login_result.status) {
        case ACTION_LOGINED_SUCCESSFULLY:
            return 2;
        case ACTION_LOGINED_FAILED:
            puts(login_result.message);
            return 0;
        case ACTION_FETCHING_FAILED:
            return 3;
        default:
            assert(0);
            exit(1);
            break;
    }
}
